//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//  Floor animation: raising stairs.
//



#include "z_zone.h"
#include "doomdef.h"
#include "p_local.h"

#include "s_sound.h"

// State.
#include "doomstat.h"
#include "r_state.h"
// Data.
#include "sounds.h"


//
// FLOORS
//

//
// Move a plane (floor or ceiling) and check for crushing
//
result_e
T_MovePlane
( sector_t* sector,
  fixed_t   speed,
  fixed_t   dest,
  boolean   crush,
  int       floorOrCeiling,
  int       direction )
{
    boolean flag;
    fixed_t lastpos;

    switch(floorOrCeiling)
    {
      case 0:
    // FLOOR
    switch(direction)
    {
      case -1:
        // DOWN
        if (sector->floorheight - speed < dest)
        {
        lastpos = sector->floorheight;
        sector->floorheight = dest;
        flag = P_ChangeSector(sector,crush);
        if (flag == true)
        {
            sector->floorheight =lastpos;
            P_ChangeSector(sector,crush);
            //return crushed;
        }
        return pastdest;
        }
        else
        {
        lastpos = sector->floorheight;
        sector->floorheight -= speed;
        flag = P_ChangeSector(sector,crush);
        if (flag == true)
        {
            sector->floorheight = lastpos;
            P_ChangeSector(sector,crush);
            return crushed;
        }
        }
        break;

      case 1:
        // UP
        if (sector->floorheight + speed > dest)
        {
        lastpos = sector->floorheight;
        sector->floorheight = dest;
        flag = P_ChangeSector(sector,crush);
        if (flag == true)
        {
            sector->floorheight = lastpos;
            P_ChangeSector(sector,crush);
            //return crushed;
        }
        return pastdest;
        }
        else
        {
        // COULD GET CRUSHED
        lastpos = sector->floorheight;
        sector->floorheight += speed;
        flag = P_ChangeSector(sector,crush);
        if (flag == true)
        {
            if (crush == true)
            return crushed;
            sector->floorheight = lastpos;
            P_ChangeSector(sector,crush);
            return crushed;
        }
        }
        break;
    }
    break;

      case 1:
    // CEILING
    switch(direction)
    {
      case -1:
        // DOWN
        if (sector->ceilingheight - speed < dest)
        {
        lastpos = sector->ceilingheight;
        sector->ceilingheight = dest;
        flag = P_ChangeSector(sector,crush);

        if (flag == true)
        {
            sector->ceilingheight = lastpos;
            P_ChangeSector(sector,crush);
            //return crushed;
        }
        return pastdest;
        }
        else
        {
        // COULD GET CRUSHED
        lastpos = sector->ceilingheight;
        sector->ceilingheight -= speed;
        flag = P_ChangeSector(sector,crush);

        if (flag == true)
        {
            if (crush == true)
            return crushed;
            sector->ceilingheight = lastpos;
            P_ChangeSector(sector,crush);
            return crushed;
        }
        }
        break;

      case 1:
        // UP
        if (sector->ceilingheight + speed > dest)
        {
        lastpos = sector->ceilingheight;
        sector->ceilingheight = dest;
        flag = P_ChangeSector(sector,crush);
        if (flag == true)
        {
            sector->ceilingheight = lastpos;
            P_ChangeSector(sector,crush);
            //return crushed;
        }
        return pastdest;
        }
        else
        {
        lastpos = sector->ceilingheight;
        sector->ceilingheight += speed;
        flag = P_ChangeSector(sector,crush);
// UNUSED
#if 0
        if (flag == true)
        {
            sector->ceilingheight = lastpos;
            P_ChangeSector(sector,crush);
            return crushed;
        }
#endif
        }
        break;
    }
    break;

    }
    return ok;
}


//
// MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN)
//
void T_MoveFloor(floormove_t* floor)
{
    result_e    res;

    res = T_MovePlane(floor->sector,
              floor->speed,
              floor->floordestheight,
              floor->crush,0,floor->direction);

    if (!(leveltime&7))
    S_StartSound(&floor->sector->soundorg, sfx_stnmov);

    if (res == pastdest)
    {
    floor->sector->specialdata = NULL;

    if (floor->direction == 1)
    {
        switch(floor->type)
        {
          case donutRaise:
        floor->sector->special = floor->newspecial;
        floor->sector->floorpic = floor->texture;
          default:
        break;
        }
    }
    else if (floor->direction == -1)
    {
        switch(floor->type)
        {
          case lowerAndChange:
        floor->sector->special = floor->newspecial;
        floor->sector->floorpic = floor->texture;
          default:
        break;
        }
    }
    P_RemoveThinker(&floor->thinker);

    S_StartSound(&floor->sector->soundorg, sfx_pstop);
    }

}

//
// HANDLE FLOOR TYPES
//
int
EV_DoFloor
( line_t*   line,
  floor_e   floortype )
{
    int         secnum;
    int         rtn;
    int         i;
    sector_t*       sec;
    floormove_t*    floor;

    secnum = -1;
    rtn = 0;
    while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)
    {
    sec = &sectors[secnum];

    // ALREADY MOVING?  IF SO, KEEP GOING...
    if (sec->specialdata)
        continue;

    // new floor thinker
    rtn = 1;
    floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);
    P_AddThinker (&floor->thinker);
    sec->specialdata = floor;
    floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;
    floor->type = floortype;
    floor->crush = false;

    switch(floortype)
    {
      case lowerFloor:
        floor->direction = -1;
        floor->sector = sec;
        floor->speed = FLOORSPEED;
        floor->floordestheight =
        P_FindHighestFloorSurrounding(sec);
        break;

      case lowerFloorToLowest:
        floor->direction = -1;
        floor->sector = sec;
        floor->speed = FLOORSPEED;
        floor->floordestheight =
        P_FindLowestFloorSurrounding(sec);
        break;

      case turboLower:
        floor->direction = -1;
        floor->sector = sec;
        floor->speed = FLOORSPEED * 4;
        floor->floordestheight =
        P_FindHighestFloorSurrounding(sec);
        if (floor->floordestheight != sec->floorheight)
        floor->floordestheight += 8*FRACUNIT;
        break;

      case raiseFloorCrush:
        floor->crush = true;
      case raiseFloor:
        floor->direction = 1;
        floor->sector = sec;
        floor->speed = FLOORSPEED;
        floor->floordestheight =
        P_FindLowestCeilingSurrounding(sec);
        if (floor->floordestheight > sec->ceilingheight)
        floor->floordestheight = sec->ceilingheight;
        floor->floordestheight -= (8*FRACUNIT)*
        (floortype == raiseFloorCrush);
        break;

      case raiseFloorTurbo:
        floor->direction = 1;
        floor->sector = sec;
        floor->speed = FLOORSPEED*4;
        floor->floordestheight =
        P_FindNextHighestFloor(sec,sec->floorheight);
        break;

      case raiseFloorToNearest:
        floor->direction = 1;
        floor->sector = sec;
        floor->speed = FLOORSPEED;
        floor->floordestheight =
        P_FindNextHighestFloor(sec,sec->floorheight);
        break;

      case raiseFloor24:
        floor->direction = 1;
        floor->sector = sec;
        floor->speed = FLOORSPEED;
        floor->floordestheight = floor->sector->floorheight +
        24 * FRACUNIT;
        break;
      case raiseFloor512:
        floor->direction = 1;
        floor->sector = sec;
        floor->speed = FLOORSPEED;
        floor->floordestheight = floor->sector->floorheight +
        512 * FRACUNIT;
        break;

      case raiseFloor24AndChange:
        floor->direction = 1;
        floor->sector = sec;
        floor->speed = FLOORSPEED;
        floor->floordestheight = floor->sector->floorheight +
        24 * FRACUNIT;
        sec->floorpic = line->frontsector->floorpic;
        sec->special = line->frontsector->special;
        break;

      case raiseToTexture:
      {
          int   minsize = INT_MAX;
          side_t*   side;

          floor->direction = 1;
          floor->sector = sec;
          floor->speed = FLOORSPEED;
          for (i = 0; i < sec->linecount; i++)
          {
          if (twoSided (secnum, i) )
          {
              side = getSide(secnum,i,0);
              if (side->bottomtexture >= 0)
              if (textureheight[side->bottomtexture] <
                  minsize)
                  minsize =
                  textureheight[side->bottomtexture];
              side = getSide(secnum,i,1);
              if (side->bottomtexture >= 0)
              if (textureheight[side->bottomtexture] <
                  minsize)
                  minsize =
                  textureheight[side->bottomtexture];
          }
          }
          floor->floordestheight =
          floor->sector->floorheight + minsize;
      }
      break;

      case lowerAndChange:
        floor->direction = -1;
        floor->sector = sec;
        floor->speed = FLOORSPEED;
        floor->floordestheight =
        P_FindLowestFloorSurrounding(sec);
        floor->texture = sec->floorpic;

        for (i = 0; i < sec->linecount; i++)
        {
        if ( twoSided(secnum, i) )
        {
            if (getSide(secnum,i,0)->sector-sectors == secnum)
            {
            sec = getSector(secnum,i,1);

            if (sec->floorheight == floor->floordestheight)
            {
                floor->texture = sec->floorpic;
                floor->newspecial = sec->special;
                break;
            }
            }
            else
            {
            sec = getSector(secnum,i,0);

            if (sec->floorheight == floor->floordestheight)
            {
                floor->texture = sec->floorpic;
                floor->newspecial = sec->special;
                break;
            }
            }
        }
        }
      default:
        break;
    }
    }
    return rtn;
}




//
// BUILD A STAIRCASE!
//
int
EV_BuildStairs
( line_t*   line,
  stair_e   type )
{
    int         secnum;
    int         height;
    int         i;
    int         newsecnum;
    int         texture;
    int         ok;
    int         rtn;

    sector_t*       sec;
    sector_t*       tsec;

    floormove_t*    floor;

    fixed_t     stairsize = 0;
    fixed_t     speed = 0;

    secnum = -1;
    rtn = 0;
    while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)
    {
    sec = &sectors[secnum];

    // ALREADY MOVING?  IF SO, KEEP GOING...
    if (sec->specialdata)
        continue;

    // new floor thinker
    rtn = 1;
    floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);
    P_AddThinker (&floor->thinker);
    sec->specialdata = floor;
    floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;
    floor->direction = 1;
    floor->sector = sec;
    switch(type)
    {
      case build8:
        speed = FLOORSPEED/4;
        stairsize = 8*FRACUNIT;
        break;
      case turbo16:
        speed = FLOORSPEED*4;
        stairsize = 16*FRACUNIT;
        break;
    }
    floor->speed = speed;
    height = sec->floorheight + stairsize;
    floor->floordestheight = height;

    texture = sec->floorpic;

    // Find next sector to raise
    // 1.   Find 2-sided line with same sector side[0]
    // 2.   Other side is the next sector to raise
    do
    {
        ok = 0;
        for (i = 0;i < sec->linecount;i++)
        {
        if ( !((sec->lines[i])->flags & ML_TWOSIDED) )
            continue;

        tsec = (sec->lines[i])->frontsector;
        newsecnum = tsec-sectors;

        if (secnum != newsecnum)
            continue;

        tsec = (sec->lines[i])->backsector;
        newsecnum = tsec - sectors;

        if (tsec->floorpic != texture)
            continue;

        height += stairsize;

        if (tsec->specialdata)
            continue;

        sec = tsec;
        secnum = newsecnum;
        floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);

        P_AddThinker (&floor->thinker);

        sec->specialdata = floor;
        floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;
        floor->direction = 1;
        floor->sector = sec;
        floor->speed = speed;
        floor->floordestheight = height;
        ok = 1;
        break;
        }
    } while(ok);
    }
    return rtn;
}

